package com.demo.common.mq;

import com.demo.common.exceptions.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 消息处理器
 */
@Component
@Slf4j
public class MessageHandler implements BeanPostProcessor {
    /**
     * 队列和消费方法缓存
     */
    private Map<String, MessageHandleMethod> handleMethodMap=new HashMap<>();

    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class<?> userClass = ClassUtils.getUserClass(bean);
        //找到所有带MQListener注解的方法
        List<Method> methodsListWithAnnotation = MethodUtils.getMethodsListWithAnnotation(userClass, MQListener.class);
        if(!CollectionUtils.isEmpty(methodsListWithAnnotation)){
            for (Method method : methodsListWithAnnotation) {
                //消费方法必须是method(String)的形式，单参数，参数类型String
                if(method.getParameterCount()==1&&method.getParameterTypes()[0].equals(String.class)){
                    MQListener mqListener = method.getAnnotation(MQListener.class);
                    MessageHandleMethod oldMethod = handleMethodMap.get(mqListener.queue());
                    //同队列有不同的消费方法，报错
                    if(oldMethod!=null){
                        String oldMethodPath=oldMethod.getMethod().getDeclaringClass().getName()+"#"+oldMethod.getMethod().getName();
                        String currentMethodPath=userClass.getName()+"#"+method.getName();
                        String errorMsg= MessageFormat.format("MQ监听队列{}处理方法重复,重复方法是{}和{}",mqListener.queue(),oldMethodPath,currentMethodPath);
                        throw new ServiceException(errorMsg);
                    }
                    MessageHandleMethod messageHandleMethod=new MessageHandleMethod(method,bean);
                    handleMethodMap.put(mqListener.queue(), messageHandleMethod);
                }else{
                    log.warn("MQ监听处理方法校验失败，方法必须是单参数且参数类型为String的方法，校验失败的方法是{}#{}",userClass.getName(),method.getName());
                }
            }
        }
        return bean;
    }



    /**
     * 是否支持处理该队列的消息
     * @param queue
     * @return
     */
    public boolean support(String queue){
        return handleMethodMap.containsKey(queue);
    }

    /**
     * 消息处理
     * @param queue
     * @param msg
     */
    public boolean handleMsg(String queue,String msg){
        MessageHandleMethod handleMethod = handleMethodMap.get(queue);
        if(handleMethod!=null){
            try {
                //反射处理message
                handleMethod.getMethod().invoke(handleMethod.getTargetBean(),msg);
                log.info("处理队列{}消息{}成功",queue,msg);
                return true;
            } catch (Exception e) {
                log.error("处理队列{}消息{}失败",queue,msg,e);
                return false;
            }
        }else{
            log.warn("queue:{}没有消费者,消息丢弃",queue);
            return true;
        }
    }


}
